package com.lincoln.framework.utils;

import com.lincoln.framework.common.Constant;
import com.lincoln.framework.exception.FileException;
import eu.medsea.mimeutil.MimeUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;

import java.io.*;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 文件相关
 * 
 * @author zwm
 * 
 */
public class FileUtilsEx {

	protected static FrameworkLogger logger = FrameworkLogger.initFrameworkLogger(FileUtilsEx.class);

	public static final String IMAGE_TYPE_LIST = ",jpg,jpeg,png,gif,tif,bmp,jpe,";

	public static final String AUDIO_TYPE_LIST = ",mp3,wma,wav,amr,";

	public static final String VIDEO_TYPE_LIST = ",mp4,rm,rmvb,flv,avi,mpg,mpeg,wmv,mov,";
	
	private static Pattern FilePattern = Pattern.compile("[\\\\/:*?\"<>|]");  

	public static enum SEARCH_MODE {
		EQ, STARTWITH, ENDWITH, CONTAIN
	}

	// 缓存文件头信息-文件头信息
	public static final HashMap<String, String> mFileTypes = new HashMap<String, String>();
	static {
		// images
		mFileTypes.put("FFD8FF", "image/jpeg");
		mFileTypes.put("89504E47", "image/png");
		mFileTypes.put("47494638", "image/gif");
		mFileTypes.put("49492A00", "image/tiff");
		mFileTypes.put("424D", "image/bmp");
		// CAD
		mFileTypes.put("41433130", "image/vnd.dwg");
		mFileTypes.put("38425053", "image/vnd.adobe.photoshop");
		// 日记本
		mFileTypes.put("7B5C727466", "text/rtf");
		mFileTypes.put("3C3F786D6C", "text/xml");
		mFileTypes.put("68746D6C3E", "text/html");
		// 邮件
		mFileTypes.put("44656C69766572792D646174653A", "message/rfc822");
		mFileTypes.put("D0CF11E0", "application/msword");
		mFileTypes.put("5374616E64617264204A", "mdb");
		mFileTypes.put("252150532D41646F6265", "ps");
		mFileTypes.put("255044462D312E", "pdf");
		mFileTypes.put("504B0304", "docx");
		mFileTypes.put("52617221", "rar");
		mFileTypes.put("57415645", "audio/wav");
		mFileTypes.put("41564920", "video/x-msvideo");
		mFileTypes.put("2E524D46", "rm");
		mFileTypes.put("000001BA", "video/mpeg");
		mFileTypes.put("000001B3", "video/mpeg");
		mFileTypes.put("49443303", "audio/mpeg");
		mFileTypes.put("49443304", "audio/mpeg");
		mFileTypes.put("6D6F6F76", "video/quicktime");
		mFileTypes.put("3026B2758E66CF11", "video/x-ms-asf");
		mFileTypes.put("4D546864", "audio/midi");
		mFileTypes.put("1F8B08", "gz");
		mFileTypes.put("4D5A9000", "exe/dll");
		mFileTypes.put("75736167", "txt");
	}
	
	public static Map<String, ArrayList<String>> mimextensionMap = new HashMap<String, ArrayList<String>>();

	/**
	 * 常见文件头信息
	 */
	private static final Map<String, String> FILE_TYPE_MAP = new HashMap<String, String>() {
		{
			// 图片
			put("FFD8FF", "jpg");// JPEG (jpg)
			put("89504E47", "png");// PNG (png)
			put("47494638", "gif");// GIF (gif)
			put("49492A00", "tif");// TIFF (tif)
			put("424D", "bmp");// bmp
		}
	};

	/**
	 * 得到上传文件的文件头
	 * 
	 * @param src
	 * @return
	 */
	public static String bytesToHexString(byte[] src) {
		StringBuilder stringBuilder = new StringBuilder();
		if (src == null || src.length <= 0) {
			return null;
		}
		for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}

	/**
	 * 根据制定文件的文件头判断其文件类型
	 * 
	 * @param filePath
	 * @return
	 */
	public static String getFileType(String filePath) {
		String res = null;
		try {
			FileInputStream is = new FileInputStream(filePath);
			byte[] b = new byte[10];
			is.read(b, 0, b.length);
			String fileCode = bytesToHexString(b);

			// 这种方法在字典的头代码不够位数的时候可以用但是速度相对慢一点
			Iterator<String> keyIter = FILE_TYPE_MAP.keySet().iterator();
			while (keyIter.hasNext()) {
				String key = keyIter.next();
				if (key.toLowerCase().startsWith(fileCode.toLowerCase())
						|| fileCode.toLowerCase().startsWith(key.toLowerCase())) {
					res = FILE_TYPE_MAP.get(key);
					break;
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return res;
	}

	/**
	 * 判断文件头部信息，是否是图片类型
	 * 
	 * @param filePath
	 * @return
	 */
	public static boolean checkFileIsImage(String filePath) {
		return IMAGE_TYPE_LIST.contains("," + getFileType(filePath) + ",");
	}

	/**
	 * 判断文件后缀是否是图片类型
	 * 
	 * @param filePath
	 * @return
	 */
	public static boolean fileSuffixIsImageType(String filePath) {
		int lastIndexOfDot = filePath.lastIndexOf(".");
		if (lastIndexOfDot > 0 && lastIndexOfDot != filePath.length() - 1) {
			return IMAGE_TYPE_LIST.contains("," + filePath.substring(lastIndexOfDot + 1).toLowerCase() + ",");
		} else {
			return false;
		}
	}

	/**
	 * 判断文件后缀是否是语音类型
	 * 
	 * @param filePath
	 * @return
	 */
	public static boolean fileSuffixIsAudioType(String filePath) {
		int lastIndexOfDot = filePath.lastIndexOf(".");
		if (lastIndexOfDot > 0 && lastIndexOfDot != filePath.length() - 1) {
			return AUDIO_TYPE_LIST.contains("," + filePath.substring(lastIndexOfDot + 1).toLowerCase() + ",");
		} else {
			return false;
		}
	}

	/**
	 * 判断文件后缀是否是视频类型
	 * 
	 * @param filePath
	 * @return
	 */
	public static boolean fileSuffixIsVideoType(String filePath) {
		int lastIndexOfDot = filePath.lastIndexOf(".");
		if (lastIndexOfDot > 0 && lastIndexOfDot != filePath.length() - 1) {
			return VIDEO_TYPE_LIST.contains("," + filePath.substring(lastIndexOfDot + 1).toLowerCase() + ",");
		} else {
			return false;
		}
	}

	/**
	 * 注册获取文件类型
	 * 
	 * @param file
	 * @return
	 * @Exception
	 */
	public static String getMimeType(File file){
		registerMimeDetector();
		Collection<?> mimeTypes = MimeUtil.getMimeTypes(file);
		return mimeTypes.toString();
	}

	/**
	 * 注册mime探测器
	 */
	private static void registerMimeDetector() {
		if (MimeUtil.getMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector") == null) {
			MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
		}
	}

	/**
	 * 注册获取文件扩展名
	 * 
	 * @param file
	 * @return
	 * @Exception
	 */
	public static String getFileExtension(File file, String fileName) throws Exception {
		registerMimeDetector();
		String mimeType = getMimeType(file);
		Map<String, ArrayList<String>> map = getMimextensionMap();
		List<String> extensions = map.get(mimeType);
		if (extensions != null) {
			if (!ParameterChecker.isNullOrEmpty(fileName)) {
				String extension = MimeUtil.getExtension(fileName);
				if (!ParameterChecker.isNullOrEmpty(extension) && extensions.contains(extension)) {
					return extension;
				}
			}
			return extensions.get(0);
		}
		return FilenameUtils.getExtension(fileName);
	}

	/**
	 * 获取mime对应扩展数据
	 * 
	 * @return
	 * @throws IOException
	 *             Map<String,ArrayList<String>>
	 */
	private static Map<String, ArrayList<String>> getMimextensionMap() throws IOException {
		if (mimextensionMap.size() == 0) {
			File extension = new File(StringUtilsEx.getContextPath() + "/files/fileextension/extension.txt");
			File mimetype = new File(StringUtilsEx.getContextPath() + "/files/fileextension/mimetype.txt");
			BufferedReader mimetypeReader = new BufferedReader(new FileReader(mimetype));
			BufferedReader extensionReader = new BufferedReader(new FileReader(extension));

			String mimetypeStr = mimetypeReader.readLine();

			JSONObject object = new JSONObject();
			while (!ParameterChecker.isNullOrEmpty(mimetypeStr)) {
				JSONArray array = new JSONArray();
				if (object.containsKey(mimetypeStr)) {
					array = object.getJSONArray(mimetypeStr);
				}
				array.add(extensionReader.readLine());
				object.put(mimetypeStr, array);
				mimetypeStr = mimetypeReader.readLine();
			}

			mimextensionMap = (HashMap<String, ArrayList<String>>) JSONObject
					.toBean(object, mimextensionMap.getClass());
		}
		return mimextensionMap;
	}

	/**
	 * 根据文件路径获取文件头信息
	 * 
	 * @param file
	 *            文件路径
	 * @return 文件头信息
	 */
	public static String getFileMime(File file) {
		for (int i = 2; i < 14; i++) {
			String mimeType = mFileTypes.get(getFileHeader(file, i));
			if (mimeType != null) {
				return mimeType;
			}
		}
		return "application/octet-stream";
	}

	/**
	 * 文件删除，包含文件夹
	 * 
	 * @param file
	 */
	public static void deleteFile(File file) {
		if (file.exists()) {
			if (file.isDirectory()) {
				for (File itemFile : file.listFiles()) {
					deleteFile(itemFile);
				}
			}
			file.delete();
		}
	}

	/**
	 * 删除图片
	 * 
	 * @param src
	 */
	public static void deleteFile(String src) {
		if (!StringUtilsEx.isNullOrEmpty(src)) {
			File file = new File(StringUtilsEx.getContextPath() + src);
			if (file.exists()) {
				file.delete();
			}
		}
	}

	/**
	 * 删除当前目录、当前文件目录下，以特定字符开头文件
	 * 
	 * @param filePath
	 * @param startWith
	 *            void
	 */
	public static void deleteStartWith(String filePath, String startWith) {
		if (ParameterChecker.isNullOrEmpty(filePath)) {
			return;
		}
		File destFile = new File(filePath);
		if (destFile.exists()) {
			if (destFile.isFile()) {
				destFile = destFile.getParentFile();
			}
			File[] files = destFile.listFiles();
			for (File file : files) {
				if (file.isFile() && file.getName().startsWith(startWith)) {
					FileUtils.deleteQuietly(file);
				}
			}
		}
	}

	/**
	 * 保存临时图片到指定目录
	 * 
	 * @param src
	 * @param newPath
	 * @param isSelectLibPic
	 *            （是否是选择图片素材 如果是也做一次拷贝）
	 * @throws IOException
	 */
	public static String saveFile(String src, String newPath, boolean isSelectLibPic) throws Exception {
		File srcFile = new File(StringUtilsEx.getContextPath() + src);
		if (!srcFile.exists()) {
			logger.error("要保存的文件已经不存在,文件相对路径：" + src + "文件绝对路径：" + StringUtilsEx.getContextPath() + src);
			throw new FileException("要保存的文件已经不存在");
		}
		boolean condition = src.startsWith(Constant.TEMP_DIR) || src.startsWith(Constant.UEDITOR_TEMP_DIR);
		if (src != null && condition) {
			String fileName = src.substring(src.lastIndexOf("/") + 1);
			File destFile = new File(StringUtilsEx.getContextPath() + newPath + fileName);
			FileUtils.copyFile(srcFile, destFile);
			return newPath + fileName;
		} else {
			return src;
		}
	}

	/**
	 * 保存临时图片到指定目录
	 * 
	 * @param src
	 * @param newPath
	 * @throws IOException
	 */
	public static String saveFile(String src, String newPath) throws Exception {
		return saveFile(src, newPath, false);
	}

	/**
	 * 根据文件路径获取文件头信息
	 * 
	 * @param file
	 *            文件路径
	 * @return 文件头信息
	 */
	private static String getFileHeader(File file, int headLength) {
		FileInputStream is = null;
		String value = null;
		try {
			is = new FileInputStream(file);
			byte[] b = new byte[headLength];
			is.read(b, 0, b.length);
			value = bytesToHexString(b);
		} catch (Exception e) {
		} finally {
			if (null != is) {
				try {
					is.close();
				} catch (IOException e) {
				}
			}
		}
		return value;
	}

	/**
	 * 创建目录
	 * 
	 * @param directory
	 *            父目录
	 * @param subDirectory
	 *            子目录
	 */
	public static void createDirectory(String directory, String subDirectory) {
		String dir[];
		File fl = new File(directory);
		if (subDirectory == "" && fl.exists() != true) {
			fl.mkdir();
		} else if (subDirectory != "") {
			dir = subDirectory.replace('\\', '/').split("/");
			for (int i = 0; i < dir.length; i++) {
				File subFile = new File(directory + File.separator + dir[i]);
				if (subFile.exists() == false) {
					subFile.mkdir();
				}
				directory += File.separator + dir[i];
			}
		}
	}

	/**
	 * 搜索指定目录文件
	 * 
	 * @param directory
	 *            搜索目录
	 * @param searchValue
	 *            搜索文件名称
	 * @param mode
	 *            匹配模式，为null查询所有
	 * @return List<File>
	 */
	public static List<File> searchFile(String directory, String searchValue, SEARCH_MODE mode) {
		List<File> fileList = new ArrayList<File>();
		File fileDir = new File(directory);
		if (fileDir.exists() && fileDir.isDirectory()) {
			File[] files = fileDir.listFiles();
			for (File file : files) {
				if (file.isFile()) {
					switch (mode) {
					case ENDWITH:
						if (file.getName().endsWith(searchValue)) {
							fileList.add(file);
						}
						break;
					case EQ:
						if (file.getName().equals(searchValue)) {
							fileList.add(file);
						}
						break;
					case CONTAIN:
						if (file.getName().indexOf(searchValue) != -1) {
							fileList.add(file);
						}
						break;
					case STARTWITH:
						if (file.getName().startsWith(searchValue)) {
							fileList.add(file);
						}
						break;
					default:
						fileList.add(file);
						break;
					}
				}
			}
		}
		return fileList;
	}

	/**
	 * 图片mime与后缀映射关系
	 */
	private static final HashMap<String, String> MIMETYPE_SUFFIX = new HashMap<String, String>();
	static {
		MIMETYPE_SUFFIX.put("image/bmp", "bmp");
		MIMETYPE_SUFFIX.put("image/gif", "gif");
		MIMETYPE_SUFFIX.put("image/ief", "ief");
		MIMETYPE_SUFFIX.put("image/jpeg", "jpg");
		MIMETYPE_SUFFIX.put("image/png", "png");
		MIMETYPE_SUFFIX.put("image/tiff", "tiff");
		MIMETYPE_SUFFIX.put("image/vnd.djvu", "djv");
		MIMETYPE_SUFFIX.put("image/vnd.wap.wbmp", "wbmp");
		MIMETYPE_SUFFIX.put("image/x-cmu-raster", "ras");
		MIMETYPE_SUFFIX.put("image/x-portable-anymap", "pnm");
		MIMETYPE_SUFFIX.put("image/x-portable-bitmap", "pbm");
		MIMETYPE_SUFFIX.put("image/x-portable-graymap", "pgm");
		MIMETYPE_SUFFIX.put("image/x-portable-pixmap", "ppm");
		MIMETYPE_SUFFIX.put("image/x-rgb", "rgb");
		MIMETYPE_SUFFIX.put("image/x-xbitmap", "xbm");
		MIMETYPE_SUFFIX.put("image/x-xwindowdump", "xwd");
	}
	
	/**
	 * 过滤非法文件名字符
	 * @param str
	 * @return
	 */
	public static String filenameFilter(String str) {  
	    return str==null?null:FilePattern.matcher(str).replaceAll("");  
	}  
}
